﻿using HenuAlbum.ControllerModels;
using HenuAlbum.DbContext;
using HenuAlbum.Identity;
using HenuAlbum.Models;
using HenuAlbum.ResponseModels;
using HenuAlbum.Utils;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using SixLabors.ImageSharp;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;

namespace HenuAlbum.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class ResourceController : ControllerBase
    {
        private readonly ILogger<ResourceController> _logger;
        private readonly UserManager<User> _userManager;
        private readonly AppDbContext _appDbContext;
        private readonly ResourceConfig _resourceConfig;
        public ResourceController(
            ILogger<ResourceController> logger,
            UserManager<User> userManager,
            AppDbContext appDbContext,
            IOptionsMonitor<ResourceConfig> resourceConfig)
        {
            _logger = logger;
            _userManager = userManager;
            _appDbContext = appDbContext;
            _resourceConfig = resourceConfig.CurrentValue;
        }

        [HttpGet("{guid}")]
        [ResponseCache(VaryByHeader = "User-Agent", Duration = 30)]
        public async Task<IActionResult> IndexAsync(Guid guid)
        {
            // 不管什么错误都提示这个信息，迷惑爬虫
            var ErrorResponse = new NotExistsError("文件 Id 不存在.");

            // 判断 Referrer
            //string referrer = HttpContext.Request?.Headers["Referer"];
            //if (referrer == null) return BadRequest(ErrorResponse);
            //var uri = new Uri(referrer);
            //if (!_resourceConfig.AllowRefererHosts.Contains(uri.Host))
            //{
            //    return BadRequest(ErrorResponse);
            //}

            // 获取资源对象
            var resource = await _appDbContext.Resources
                .AsNoTracking()
                .Include(x => x.Album)
                .Where(x => x.Id == guid)
                .FirstOrDefaultAsync();
            if (resource == null)
            {
                return BadRequest(ErrorResponse);
            }

            if (resource.IsLocked)
            {
                return BadRequest(ErrorResponse);
            }

            // 让访问量+1
            _ = await _appDbContext.Database
                .ExecuteSqlRawAsync("Update Resources Set DownloadCount = DownloadCount + 1 Where Id = {0};", resource.Id);

            if (!resource.Album.IsPublic)
            {
                if (!User.IsInRole("管理员") && !User.IsInRole("辅导员"))
                {
                    return BadRequest(ErrorResponse);
                }
            }

            if (resource.Album is PersonalAlbum p && p.ReviewState != ReviewState.Reviewed)
            {
                if (!User.IsInRole("管理员") && !User.IsInRole("辅导员"))
                {
                    return BadRequest(ErrorResponse);
                }
            }

            string path = resource.Type switch
            {
                ResourceType.Picture => _resourceConfig.PicturePath,
                ResourceType.Video => _resourceConfig.VideoPath,
                ResourceType.Document => _resourceConfig.DocumentPath,
                _ => throw new NotImplementedException(),
            };
            FileStream fileStream = System.IO.File.OpenRead(Path.Combine(path, resource.DiskFileName));

            new FileExtensionContentTypeProvider().TryGetContentType(resource.DiskFileName, out string contentType);
            contentType ??= "application/octet-stream";

            return File(fileStream, contentType);
        }

        [Authorize]
        [HttpPost("Upload")]
        public async Task<IActionResult> UploadAsync(IFormCollection data, IFormFile file)
        {
            if (file == null || file.Length == 0)
            {
                return BadRequest(new IncorrectError("上传文件不能为空！"));
            }

            // 将要用的值取出来，如果不存在提前响应 500，免得浪费计算时间
            DateTime? shootingDate = null;
            if (DateTime.TryParse(data["shootingDate"], out DateTime tempShootDate))
            {
                shootingDate = tempShootDate;
            }
            int albumId = int.Parse(data["albumId"]);
            string title = data["title"];
            string description = data["description"];
            string location = data["location"];

            ResourceTag tag = null;
            var tagIdStr = data["tagId"].FirstOrDefault();
            if (!string.IsNullOrEmpty(tagIdStr))
            {
                int tagId = int.Parse(tagIdStr);
                tag = await _appDbContext.ResourceTag.Where(x => x.Id == tagId && x.ForSelection).FirstOrDefaultAsync();
                if (tag == null)
                {
                    return BadRequest(new NotExistsError("标签不存在."));
                }
            }

            // 获取当前用户Id
            int userId = int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier));
            var user = await _appDbContext.Users
                .AsNoTracking()
                .Include(x => x.ManagedMajors)
                .Where(x => x.Id == userId)
                .FirstOrDefaultAsync();

            // 获取相册Id对应的相册对象
            var album = _appDbContext.Albums.Where(x => x.Id == albumId).FirstOrDefault();
            if (album == null)
            {
                return BadRequest(new NotExistsError("相册不存在."));
            }

            if (album is PersonalAlbum personalAlbum)
            {
                if (personalAlbum.CreatorId != user.Id)
                {
                    return BadRequest(new IncorrectError("你无法在该相册上传!"));
                }
                // 上传新图片更新审核状态为未审核
                personalAlbum.ReviewState = ReviewState.Unreviewed;
            }
            else if (album is GroupAlbum groupAlbum)
            {
                if (!User.IsInRole("管理员") && !User.IsInRole("辅导员"))
                {
                    return BadRequest(new IncorrectError("你无法在该相册上传!"));
                }
                if (User.IsInRole("辅导员"))
                {
                    var majorId = await _appDbContext.Classes
                        .AsNoTracking()
                        .Where(x => x.Id == groupAlbum.ClassId)
                        .Select(x => x.MajorId)
                        .FirstOrDefaultAsync();
                    if (!user.ManagedMajors.Any(x => x.Id == majorId))
                    {
                        return BadRequest(new IncorrectError("你无法在该相册上传!"));
                    }
                }
            }
            else if (album is SchoolAlbum schoolAlbum)
            {
                if (!User.IsInRole("管理员"))
                {
                    return BadRequest(new IncorrectError("你无法在该相册上传!"));
                }
            }
            else
            {
                return BadRequest(new UnknownError());
            }

            // 根据上传的文件名检测文件的类型
            ResourceType type = ResourceType.Picture;
            string savePath = _resourceConfig.PicturePath;
            string fileExtention = Path.GetExtension(file.FileName).Substring(1);

            if (_resourceConfig.PictureExtensions.Contains(fileExtention))
            {
                type = ResourceType.Picture;
                savePath = _resourceConfig.PicturePath;
            }
            else if (_resourceConfig.VideoExtensions.Contains(fileExtention))
            {
                type = ResourceType.Video;
                savePath = _resourceConfig.VideoPath;
            }
            else if (_resourceConfig.DocumentExtensions.Contains(fileExtention))
            {
                type = ResourceType.Document;
                savePath = _resourceConfig.DocumentPath;
            }
            else
            {
                return BadRequest(new IncorrectError("不允许上传的文件类型."));
            }

            if (type == ResourceType.Picture)
            {
                try
                {
                    var imageInfo = Image.Identify(file.OpenReadStream());
                    if (imageInfo == null)
                    {
                        return BadRequest(new IncorrectError("图片已损坏, 无法解析."));
                    }
                }
                catch (Exception ex)
                {
                    // TODO: log
                    return BadRequest(new IncorrectError("图片已损坏, 无法解析."));
                }
            }

            // 生成储存到硬盘的文件名称
            string filePath = null;
            string diskFileName = null;
            do
            {
                diskFileName = $"{Guid.NewGuid():D}.{fileExtention}";
                filePath = Path.Combine(savePath, diskFileName);
            } while (System.IO.File.Exists(filePath));

            // 保存文件到磁盘
            Task saveFileTask = null;
            using Stream fileStream = new FileStream(filePath, FileMode.Create);
            saveFileTask = file.CopyToAsync(fileStream);

            // 插入数据库条目
            var resource = new Resource()
            {
                Title = title,
                Description = description,
                UploaderId = user.Id,
                AlbumId = album.Id,
                CreateTime = DateTime.Now,
                UploadFileName = file.FileName,
                DiskFileName = diskFileName,
                FileExtension = fileExtention,
                FileSize = file.Length,
                Type = type,
                ShootingDate = shootingDate,
                Location = location
            };
            if (tag != null)
            {
                resource.Tags.Add(tag);
            }

            _appDbContext.Resources.Add(resource);
            await _appDbContext.SaveChangesAsync();

            await saveFileTask;
            return Ok(new OkResponse("上传成功！")
            {
                Data = new
                {
                    resource.Id,
                    resource.Title,
                    resource.Description,
                    resource.AlbumId,
                    resource.CreateTime,
                    resource.FileExtension,
                    resource.FileSize,
                    resource.UploadFileName,
                    resource.Type,
                    resource.UploaderId
                }
            });
        }

        [Authorize]
        [HttpPost("Update")]
        public async Task<IActionResult> UpdateResource(UpdateResourceParam param)
        {
            var resource = await _appDbContext.Resources
                .Include(x => x.Album)
                .Where(x => x.Id == param.Id)
                .FirstOrDefaultAsync();
            if (null == resource) return BadRequest(new NotExistsError("资源不存在"));
            int userId = int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier));
            var user = await _appDbContext.Users
                .AsNoTracking()
                .Include(x => x.ManagedMajors)
                .Where(x => x.Id == userId)
                .FirstOrDefaultAsync();

            //如果用户不是管理员不是辅导员且修改的资源不是自己上传的
            if (resource.Album is PersonalAlbum personalAlbum)
            {
                if (personalAlbum.CreatorId != user.Id)
                {
                    return BadRequest(new IncorrectError("没有权限操作!"));
                }
                personalAlbum.ReviewState = ReviewState.Unreviewed;
            }
            else if (resource.Album is GroupAlbum groupAlbum)
            {
                if (!User.IsInRole("管理员") && !User.IsInRole("辅导员"))
                {
                    return BadRequest(new IncorrectError("没有权限操作!"));
                }
                if (User.IsInRole("辅导员"))
                {
                    var majorId = await _appDbContext.Classes
                        .AsNoTracking()
                        .Where(x => x.Id == groupAlbum.ClassId)
                        .Select(x => x.MajorId)
                        .FirstOrDefaultAsync();
                    if (!user.ManagedMajors.Any(x => x.Id == majorId))
                    {
                        return BadRequest(new IncorrectError("没有权限操作!"));
                    }
                }
            }
            else if (resource.Album is SchoolAlbum schoolAlbum)
            {
                if (!User.IsInRole("管理员"))
                {
                    return BadRequest(new IncorrectError("没有权限操作!"));
                }
            }
            else
            {
                return BadRequest(new UnknownError());
            }

            resource.Description = param.Description;
            resource.Title = param.Title;
            await _appDbContext.SaveChangesAsync();
            return Ok(new OkResponse()
            {
                Data = new
                {
                    resource.Id,
                    resource.Title,
                    resource.Description,
                    resource.AlbumId,
                    resource.CreateTime,
                    resource.FileExtension,
                    resource.FileSize,
                    resource.UploadFileName,
                    resource.Type,
                    resource.UploaderId
                }
            });
        }
        
        [Authorize]
        [HttpPost("{guid}/Delete")]
        public async Task<IActionResult> DeleteResource(Guid guid)
        {
            var user = await _userManager.GetUserAsync(this.User);
            var roles = await _userManager.GetRolesAsync(user);

            // 获取资源
            var resource = _appDbContext.Resources.Where(x => x.Id == guid).FirstOrDefault();
            if (resource == null)
            {
                return BadRequest(new NotExistsError("资源不存在"));
            }

            //验证身份信息，如果身份信息为空或者 与资源上传者信息不一样而且又不是管理员时，返回错误
            if (!roles.Where(x => Constants.SuperRoles.Contains(x)).Any() && user.Id != resource.UploaderId)
            {
                return BadRequest(new IncorrectError("没有权限!"));
            }
            _appDbContext.Resources.Remove(resource);
            await _appDbContext.SaveChangesAsync();
            return Ok(new OkResponse());
        }
        [Authorize]
        [HttpPost("DeleteTag")]
        public async Task<IActionResult> DeleteResourceTag(DeleteResourceTagParams param)
        {

            var resource = await _appDbContext.Resources
                .Include(x => x.Tags)
                .Where(x => x.Id == param.ResourceId)
                .FirstOrDefaultAsync();


            if (null == resource)
            {
                return BadRequest(new IncorrectError("资源不存在"));
            }
            var user = await _userManager.GetUserAsync(this.User);
            if (user.Id != resource.UploaderId)
            {
                return BadRequest(new IncorrectError("权限不允许"));
            }
            resource.Tags.RemoveAll(x => x.Id == param.TagId);
            await _appDbContext.SaveChangesAsync();
            return Ok(new OkResponse());
        }
        [Authorize]
        [HttpPost("AddTag")]
        public async Task<IActionResult> AddTag(AddTagParam param)
        {
            /*
             * TODO:
             *TagName接入敏感词检测     
             */

            var resource = await _appDbContext.Resources
                .Include(x => x.Tags)
                .Where(x => x.Id == param.Resource)
                .FirstOrDefaultAsync();
            if (resource == null)
            {
                return BadRequest(new NotExistsError("资源不存在."));
            }

            var user = await _userManager.GetUserAsync(this.User);
            var roles = await _userManager.GetRolesAsync(user);
            if (!roles.Where(x => Constants.SuperRoles.Contains(x)).Any() && user.Id != resource.UploaderId)
            {
                return BadRequest(new IncorrectError("没有权限!"));
            }

            ResourceTag tag = null;
            if (param.TagId != null)
            {
                tag = await _appDbContext.ResourceTag.Where(x => x.Id == param.TagId && x.ForSelection).FirstOrDefaultAsync();
                if (tag == null)
                {
                    return BadRequest(new NotExistsError("标签不存在."));
                }
            }
            else
            {
                tag = new ResourceTag()
                {
                    Name = param.Tag,
                    Description = param.Description ?? string.Empty
                };
            }

            if (resource.Tags.Any(x => x.Id == tag.Id))
            {
                return BadRequest(new AlreadyExistsError("标签已经存在."));
            }

            resource.Tags.Add(tag);
            await _appDbContext.SaveChangesAsync();
            return Ok(new OkResponse());
        }

        [Authorize]
        [Authorize(Roles = "管理员")]
        [HttpPost("{guid}/Lock")]
        public async Task<IActionResult> Lock(Guid guid)
        {
            var resource = await _appDbContext.Resources.Where(x => x.Id == guid).FirstOrDefaultAsync();
            if (resource == null) return BadRequest(new IncorrectError("资源不存在."));
            resource.IsLocked = true;
            await _appDbContext.SaveChangesAsync();
            return Ok(new OkResponse());
        }

        [Authorize]
        [Authorize(Roles = "管理员")]
        [HttpPost("{guid}/Unlock")]
        public async Task<IActionResult> Unlock(Guid guid)
        {
            var resource = await _appDbContext.Resources.Where(x => x.Id == guid).FirstOrDefaultAsync();
            if (resource == null) return BadRequest(new IncorrectError("资源不存在."));
            resource.IsLocked = false;
            await _appDbContext.SaveChangesAsync();
            return Ok(new OkResponse());
        }


        [HttpGet("{guid}/Details")]
        public async Task<ActionResult<ResourceOperatorResult>> GetResourceInfo(Guid guid)
        {
            // 不管什么错误都提示这个信息，迷惑爬虫
            var ErrorResponse = new NotExistsError("文件 Id 不存在.");

            // 获取资源对象
            var resource = await _appDbContext.Resources
                .AsNoTracking()
                .Include(x => x.Tags)
                .Include(x => x.Album)
                .Where(x => x.Id == guid)
                .FirstOrDefaultAsync();
            if (resource == null)
            {
                return BadRequest(ErrorResponse);
            }

            if (resource.IsLocked)
            {
                return BadRequest(ErrorResponse);
            }

            if (!resource.Album.IsPublic)
            {
                if (!User.IsInRole("管理员") && !User.IsInRole("辅导员"))
                {
                    return BadRequest(ErrorResponse);
                }
            }

            return Ok(new OkResponse()
            {
                Data = new
                {
                    ResourceId = resource.Id,
                    ResourceTitle = resource.Title,
                    ResourceDescription = resource.Description,
                    ShootingDate = resource.ShootingDate,
                    Location = resource.Location,
                    UploaderId = resource.UploaderId,
                    CreateTime = resource.CreateTime,
                    ResourceType = resource.Type,
                    DownloadCount = resource.DownloadCount,
                    Tags = resource.Tags.Select(x => new { x.Name, x.Id, x.Description }).ToList(),
                    Album = new
                    {
                        AlbumId = resource.Album.Id,
                        AlbumTitle = resource.Album.Title,
                        AlbumDescription = resource.Album.Description
                    }
                }
            });
        }

    }


}